package cn.newhopedairy.delivery.api;

import cn.newhopedairy.common.enums.ApiStatusCode;
import cn.newhopedairy.common.enums.UserStatusCode;
import cn.newhopedairy.common.exception.ServiceException;
import cn.newhopedairy.common.wrapper.R;
import cn.newhopedairy.delivery.api.assembly.DeliveryOrderRespAssembly;
import cn.newhopedairy.delivery.applicationc.DeliveryCmdService;
import cn.newhopedairy.delivery.applicationq.IDeliveryOrderService;
import cn.newhopedairy.delivery.command.DeclareGoodsCmd;
import cn.newhopedairy.delivery.command.DeliveryInfoCmd;
import cn.newhopedairy.delivery.infrastructure.db.po.DeliveryOrderPo;
import cn.newhopedairy.delivery.response.DeliveryOrderResp;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.redisson.api.RBoundedBlockingQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;

/**
 * 订单日计划 前端控制器
 *
 * @author Neo.Li
 * @since 2020-09-30
 */
@Slf4j
@RestController
public class DeliveryOrderController implements DeliveryOrderApi {
    @Autowired
    private IDeliveryOrderService deliveryOrderService;

    @Autowired
    private DeliveryCmdService deliveryCmdService;

    @Autowired
    private DeliveryOrderRespAssembly deliveryOrderRespAssembly;

    /**
     * 使用Springboot自带的线程池
     */
    @Qualifier("taskExecutor")
    @Autowired
    private Executor executor;

    /**
     * 报货操作队列，同时允许2笔报货操作进入队列， -- 分布式阻塞队列
     */
    private RBoundedBlockingQueue<Long> declareGoodsQueue;

    @Autowired
    public DeliveryOrderController(RedissonClient redissonClient) {
        declareGoodsQueue = redissonClient.getBoundedBlockingQueue("FreshGo:DeclareGoodsQueue");
        declareGoodsQueue.trySetCapacity(2);
    }

    @Override
    @Cacheable(cacheNames = "DeliveryOrdersByManId")
    public R<List<DeliveryOrderResp>> getDeliveryOrdersByManId(Long deliveryManId, LocalDate deliveryDate) {
        log.info("info:orders");
        log.debug("debug:orders");
        log.warn("warn:orders");
        log.error("error:orders");

        if(deliveryManId == 1) {
            throw new ServiceException(UserStatusCode.NO_USER);
        }
        //TODO 验证in语句组装问题
        List<String> noList = new ArrayList<>();
        return R.create(ApiStatusCode.SUCCESS, deliveryOrderRespAssembly.toRespList(deliveryOrderService.list(new LambdaQueryWrapper<DeliveryOrderPo>()
                .in(DeliveryOrderPo::getOrderNo, noList))));
    }

    @Override
    @Cacheable(cacheNames = "DeliveryOrdersByConsigneeId")
    public R<List<DeliveryOrderResp>> getDeliveryOrders(Long consigneeId) {
        if(consigneeId == 1) {
            throw new ServiceException(UserStatusCode.NO_CONSIGNEE);
        }
        return null;
    }

    @Override
    @Cacheable(cacheNames = "DeliveryOrderById")
    public R<DeliveryOrderResp> getDeliveryOrder(Long deliveryOrderId) {
        return R.create(ApiStatusCode.SUCCESS, deliveryOrderRespAssembly.toResp(deliveryOrderService.getById(deliveryOrderId)));
    }

    @Override
    public R<Void> declareGoodsToFactory(DeclareGoodsCmd declareGoodsCmd) {
        R<Void> result;
        //加入队列
        if(declareGoodsQueue.offer(declareGoodsCmd.getStationId())) {
            //入队列成功
            result = R.create(ApiStatusCode.QUEUING, null);
        } else {
            //入队列失败
            result = R.create(ApiStatusCode.QUEUE_FULL, null);
        }
        //TODO 触发一次队列消费
        triggerDeclareGoods();
        return result;
    }

    @Override
    public R<Void> triggerDeclareGoods() {
        executor.execute(() -> {
            Long id = declareGoodsQueue.peek();
            if(id != null) {
                log.info("开始消费：{}", id);

                //列表分片
                List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
                List<List<Integer>> subs = ListUtils.partition(intList, intList.size()/3);

                //并行处理
                CountDownLatch countDownLatch = new CountDownLatch(subs.size());
                subs.forEach(sub -> {
                    executor.execute(() -> {
                        log.info("子任务开始");
                        sub.forEach(i -> {
                            log.info("报货 {}", i);
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        });
                        countDownLatch.countDown();
                    });
                });
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    log.error("超时", e);
                }

                log.info("完成消费：{}", id);
                declareGoodsQueue.poll();
            } else {
                log.info("队列是空的");
            }
        });

        return R.create(ApiStatusCode.SUCCESS, null);
    }

    @Override
    public R<String> declareGoodsStatus(Long stationId) {
        if(stationId.equals(declareGoodsQueue.peek())) {
            return R.create(ApiStatusCode.QUEUING, "执行中");
        }

        for (Long aLong : declareGoodsQueue) {
            if (aLong.equals(stationId)) {
                return R.create(ApiStatusCode.QUEUING, "排队中");
            }
        }
        return R.create(ApiStatusCode.FAILED, "已完成，或从未提交过");
    }

    @Override
    public R<Long> createDeliveryOrder(DeliveryInfoCmd deliveryInfoCmd) {
        DeliveryOrderPo po = new DeliveryOrderPo();
        po.setReceiverMobile(deliveryInfoCmd.getPhoneNumber());
        deliveryOrderService.save(po);
        return null;
    }

    @Override
    public R<List<Long>> createDeliveryOrders(@Valid List<DeliveryInfoCmd> deliveryInfoCmds) {
        return null;
    }

    @Override
    public R<Long> deliveredDeliveryOrder(Long deliveryOrderId) {
        if(deliveryCmdService.delivered(deliveryOrderId)) {
            return R.create(ApiStatusCode.SUCCESS, deliveryOrderId);
        }
        return R.create(ApiStatusCode.FAILED, deliveryOrderId);
    }

    @Override
    public R<Long> refuseDeliveryOrder(Long deliveryOrderId) {
        if(deliveryCmdService.refuse(deliveryOrderId)) {
            return R.create(ApiStatusCode.SUCCESS, deliveryOrderId);
        }
        return R.create(ApiStatusCode.FAILED, deliveryOrderId);
    }

    @Override
    public R<Long> lackDeliveryOrder(Long deliveryOrderId) {
        if(deliveryCmdService.lack(deliveryOrderId)) {
            return R.create(ApiStatusCode.SUCCESS, deliveryOrderId);
        }
        return R.create(ApiStatusCode.FAILED, deliveryOrderId);
    }
}

